package hku.cs.eclunuo.launch;

import hku.cs.eclunuo.LunuoConstant;
import hku.cs.eclunuo.LunuoPlugin;
import hku.cs.eclunuo.marker.SusMarker;
import hku.cs.eclunuo.util.LunuoLibHelper;
import hku.cs.eclunuo.views.Ltest;
import hku.cs.eclunuo.views.LtestCase;
import hku.cs.eclunuo.views.LtestMethod;
import hku.cs.eclunuo.views.LtestSuite;
import hku.cs.eclunuo.views.RankView;
import hku.cs.eclunuo.views.TestContentProvider;
import hku.cs.lunuo.tools.DirTools;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.security.InvalidParameterException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.junit.JUnitCore;
import org.eclipse.jdt.junit.TestRunListener;
import org.eclipse.jdt.junit.model.ITestCaseElement;
import org.eclipse.jdt.junit.model.ITestElement;
import org.eclipse.jdt.junit.model.ITestRunSession;
import org.eclipse.jdt.junit.model.ITestSuiteElement;
import org.eclipse.jdt.junit.model.ITestElement.Result;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;

import soot.Scene;

public class LunuoAfterLaunchDelegate extends TestRunListener {
	private RankView viewer;
	private CheckboxTreeViewer treeViewer;
	private TableViewer tableViewer;
	private Ltest testConstruct;
	private String runName;

	private String metaDataDir;
	private URL[] sootclassPath;
	// private IJavaProject javaProject;
	private Set<String> tmpSet = new HashSet<String>();

	private File conPath = null;

	private HashMap<String, HashMap<String, Boolean>> result = new HashMap<String, HashMap<String, Boolean>>();

	public LunuoAfterLaunchDelegate(String runName, IJavaProject javaProject,
			 String rankAlgName) {
		// this.resultDirPath = resultDirPath;
		this.sootclassPath = LunuoLibHelper.getSootClassPath(javaProject);
		// this.javaProject = javaProject;
		this.metaDataDir = LunuoPlugin.METADIR.append(
				javaProject.getProject().getName()).append(
				LunuoConstant.INSTRDEFAULTOUTDIR).toOSString()
				+ File.separator;
		this.runName = runName;
		SusMarker sm = new SusMarker(javaProject);
		sm.deleteMarkers();

		try {
			this.viewer = (RankView) LunuoPlugin.getActivePage().showView(
					RankView.VIEW_ID, null, IWorkbenchPage.VIEW_VISIBLE);
		} catch (PartInitException e) {
			e.printStackTrace();
		}

		if (this.viewer == null)
			throw new InvalidParameterException();

		treeViewer = this.viewer.getTreeViewer();
		tableViewer = this.viewer.getTableViewer();
		this.viewer.setJavaProject(javaProject);
		this.viewer.setRankAlgName(rankAlgName);

		DirTools.newTest(this.metaDataDir);// fix bug, at first it is put at
		// sesstionstart, but then
		// junit create result files, so clear the directory can't be complete.
//		DirTools.delete(LunuoPlugin.METADIR.append(javaProject.getProject().getName()).append(runName).toOSString());
				
		JUnitCore.addTestRunListener(this);
	}

	@Override
	public void sessionFinished(ITestRunSession session) {

		
		
		for (String rkey : result.keySet()) {
			HashMap<String, Boolean> inResult = result.get(rkey);

			Element xmlRoot = new Element("testcase");
			xmlRoot.setAttribute("name", rkey);
			xmlRoot.setAttribute("number", Integer.toString(inResult.size()));
			Document xmlDoc = new Document(xmlRoot);

			for (String key : inResult.keySet()) {
				Element xmlTestMethod = new Element("testmethod");
				xmlTestMethod.setAttribute("name", key);
				xmlTestMethod.setAttribute("status", ((Boolean) inResult
						.get(key)).toString());
				xmlRoot.addContent(xmlTestMethod);
			}

			try {
				File f = DirTools.prepare(metaDataDir + rkey);
				File fz = new File(f, "result.xml");
				new XMLOutputter().output(xmlDoc, new FileOutputStream(fz));
				System.out.println("write result file to " + fz.toString());
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		//
		// Display.getDefault().asyncExec(new Runnable() {
		// public void run() {
		
		
		try {
			Scene.v().setSootClassPath(
					LunuoLibHelper.urlsToString(sootclassPath));
			// for (String rkey : result.keySet()) {
			// HashMap<String, Boolean> inResult = result.get(rkey);
			// String tmp = resultDirPath + rkey + File.separator;
			// for (String key : inResult.keySet()) {
			// String[] args = new String[] { tmp + key + File.separator };
			// Check.main(args);
			// }
			//				
			// // new ComputeRank().doDir(new File(tmp), null);
			// // new SuspMarker(javaProject.getElementName()).addMarkers(tmp);
			//
			// }

			
			
			

			Control ctrl = treeViewer.getControl();
			if (ctrl == null || ctrl.isDisposed())
				return;
			ctrl.getDisplay().syncExec(new Runnable() {
				public void run() {
					if (!treeViewer.getControl().isDisposed()) {
						// ((List) treeViewer.getInput()).clear();
						// treeViewer.setInput(new Object());
						// treeViewer.refresh();
						((List) tableViewer.getInput()).clear();
						tableViewer.refresh();
						//If test construct is compicate, it is hard to find the wrong testcase.
						//so we can use readTeststructure2();
//						readTeststructure(conPath);
						readTeststructure2();

						treeViewer.setInput(new Ltest[] { testConstruct });
						((TestContentProvider) treeViewer.getContentProvider())
								.updateTreeCheck(testConstruct);

						LtestCase tst = findFirstTestCaseFromSuite(testConstruct);
						List<Ltest> lTstC = tst.getChildren();
						treeViewer.setChecked(tst, true);
						
						
						for (Ltest t : lTstC) {
							treeViewer.setChecked(t, true);
							
						}
										
						try {
							viewer.fillTableViewer(tst);
							viewer.getAlgViewer().setSelection(
									new StructuredSelection(viewer
											.getRankAlgName()));
							viewer.getViewSite().getPage().showView(
									RankView.VIEW_ID);
						} catch (PartInitException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}

				

			});

		} catch (Exception ex) {
			ex.printStackTrace();
		}
		// }
		// });

		JUnitCore.removeTestRunListener(this);
		
		super.sessionFinished(session);
	}

	protected void readTeststructure2() {
		testConstruct = new LtestSuite(runName, null);
		for (String rkey : result.keySet()) {
			HashMap<String, Boolean> inResult = result.get(rkey);
			if(inResult.containsValue(false)) {
				LtestCase tc = new LtestCase(rkey, testConstruct);
				for (String key : inResult.keySet()) {
					LtestMethod etest = new LtestMethod(key, tc);
					tc.getChildren().add(etest);
				}
				testConstruct.getChildren().add(tc);
			}
		}
		
	}

	private LtestCase findFirstTestCaseFromSuite(Ltest test) {
		if (test instanceof LtestSuite) {
			List<Ltest> lChildren = test.getChildren();
			for (Ltest t : lChildren) {
				if (t instanceof LtestCase) {
					return (LtestCase) t;
				} else if (t instanceof LtestSuite) {
					return findFirstTestCaseFromSuite(t);
				}
			}

		}
		return null;

	}

	private boolean isSame(String a, String b) {
		if (a.startsWith(b)) {
			if (a.length() > b.length()) {
				if (a.charAt(b.length()) == '.')
					return true;
				else
					return false;
			} else
				return true;
		}
		return false;
	}

	public boolean constructCate(ITestElement test, Element xmlE) {
		if (test instanceof ITestCaseElement) {
			Element e = new Element("testmethod");
			String name = ((ITestCaseElement) test).getTestMethodName();

			int max = 0;
			boolean is = false;
			for (String s : tmpSet) {
				if (isSame(s, name)) {
					String[] xx = s.split("\\.");
					if (xx.length == 2) {
						if (Integer.parseInt(xx[1]) > max)
							max = Integer.parseInt(xx[1]);
					}
					is = true;
				}
			}
			if (is) {
				max++;
				name += "." + max;
			}

			e.setAttribute("name", name);
			tmpSet.add(name);
			xmlE.addContent(e);
			return false;
		} else if (test instanceof ITestSuiteElement) {
			Element e = new Element("testsuite");
			e.setAttribute("name", ((ITestSuiteElement) test)
					.getSuiteTypeName());
			xmlE.addContent(e);
			boolean isTestCase = true;
			ITestElement[] ites = ((ITestSuiteElement) test).getChildren();
			for (ITestElement ite : ites) {
				if (constructCate(ite, e)) {
					isTestCase = false;
				}
			}
			if (isTestCase && ites.length > 0) {
				e.setName("testcase");
				e.setAttribute("name", ((ITestCaseElement) ites[0])
						.getTestClassName());
				tmpSet.clear();
			}
			return true;
		}
		return true;
	}

	@Override
	public void sessionStarted(ITestRunSession session) {

		Element xmlRoot = new Element("Test_Session");
		xmlRoot.setAttribute("name", runName);
		Document xmlDoc = new Document(xmlRoot);
		ITestElement[] ites = session.getChildren();
		for (ITestElement ite : ites)
			constructCate(ite, xmlRoot);

		try {
			conPath = new File(metaDataDir, "construct.xml");
			new XMLOutputter().output(xmlDoc, new FileOutputStream(conPath));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		super.sessionStarted(session);
	}

	@Override
	public void testCaseFinished(ITestCaseElement testCaseElement) {

		HashMap<String, Boolean> inMap = result.get(testCaseElement
				.getTestClassName());

		if (inMap == null)
			inMap = new HashMap<String, Boolean>();
		// System.out.println(testCaseElement.getTestResult(true).toString());
		String name = testCaseElement.getTestMethodName();
		int max = 0;
		boolean is = false;
		for (String s : inMap.keySet()) {
			if (s.startsWith(name)) {
				String[] xx = s.split("\\.");
				if (xx.length == 2) {
					if (Integer.parseInt(xx[1]) > max)
						max = Integer.parseInt(xx[1]);
				}
				is = true;
			}
		}
		if (is) {
			max++;
			name += "." + max;
		}
		if (testCaseElement.getTestResult(true).equals(Result.OK)) {
			inMap.put(name, true);
		} else {
			inMap.put(name, false);
		}
		result.put(testCaseElement.getTestClassName(), inMap);
		super.testCaseFinished(testCaseElement);
	}

	@Override
	public void testCaseStarted(ITestCaseElement testCaseElement) {
		// TODO Auto-generated method stub
		super.testCaseStarted(testCaseElement);
	}

	private void readTestcase(Element e, Ltest t) {
		Iterator it = e.getChildren("testcase").iterator();
		while (it.hasNext()) {
			Element se = (Element) it.next();
			String name = se.getAttributeValue("name");
			// Test test = new Test(name, false, t, Test.TESTCASE);
			LtestCase test = new LtestCase(name, t);

			t.getChildren().add(test);

			Iterator i = se.getChildren("testmethod").iterator();
			while (i.hasNext()) {
				Element ese = (Element) i.next();
				String ename = ese.getAttributeValue("name");
				// Test etest = new Test(ename, false, test, Test.TESTMETHOD);
				LtestMethod etest = new LtestMethod(ename, test);
				test.getChildren().add(etest);
			}
		}
	}

	private void readTestsuite(Element e, Ltest t) {
		Iterator it = e.getChildren("testsuite").iterator();
		while (it.hasNext()) {
			Element se = (Element) it.next();
			String name = se.getAttributeValue("name");
			// Test test = new Test(name, false, t, Test.TESESUITE);
			LtestSuite test = new LtestSuite(name, t);
			t.getChildren().add(test);
			readTestsuite(se, test);
		}

		readTestcase(e, t);

	}

	private void readTeststructure(File f) {
		SAXBuilder sb = new SAXBuilder();
		try {
			Document xmlDoc = sb.build(f);
			Element xmlRoot = xmlDoc.getRootElement();

			testConstruct = new LtestSuite(xmlRoot.getAttributeValue("name"),
					null);
			readTestsuite(xmlRoot, testConstruct);

		} catch (JDOMException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	
	

}
